//
// Created by LiuYou on 2021/8/1.
//
/*
 * smart pointer: 
 * 
 * 每当需要使用智能指针时, std::unique_ptr基本上是手头首选。
 * std::unique_ptr一个常见用法是在对象继承中作为工厂函数的返回型别。
 * std::unique_ptr另一个常见用法是实现Pimpl。
 * 
 * std::unique_ptr数组形式的存在, 作为一种知识上的了解就够了。因为std::array, std::vector和std::string这些数据结构几乎总是比
 * 裸数组更好。
 * 
 * std::unique_ptr可以方便高效地转换为std::shared_ptr。
 *
 * 移动std::shared_ptr比拷贝它们要快: 拷贝要求递增引用计数, 而移动则不需要。这一点对于构造和赋值操作同样成立。
 * 所以, 移动构造函数比拷贝构造函数快, 移动赋值比拷贝赋值快。
 *
 * 尽可能避免将裸指针传递给一个std::shared_ptr的构造函数, 而是使用std::make_shared函数来产生一个std::shared_ptr。
 *
 * 对于类似std::shared_ptr但是可能 dangle 的指针使用std::weak_ptr。
 * 使用std::weak_ptr的情况现在还没有遇到过。
 *
 * 对于Pimpl写法, 一般使用std::unique_ptr。而且在*.hpp中只写函数声明。尤其是将特殊成员函数(Big-Five)的定义放到实现文件中。
 * 对于output stream function也是写到实现文件(*.cpp)中。
 *
 */
/*
以下内容来源于网络, 感谢！
Pimpl的优点：
 1. 信息隐藏。私有成员现在可以完全隐藏在公有接口之外，使得实现细节得以隐藏。头文件也更加干净、清晰便于理解。
 2. 降低耦合。 以上文例子为例，两个模块不必再重复包含头文件，把依赖项放到cpp文件中。
 3. 加速编译。
 4. 更好的二进制兼容性。采用Pimpl法的对象大小从不改变，因为对象总是单个指针的大小。实现类的修改只影响cpp实现类的大小。
 5. 惰性分配。m_cloudImp成员可以在真正使用时才new，（和代码实现有关系），我的例子中不满足此情况。

Pimpl的缺点：
 1. 需要手动释放资源。
 2. 真正执行的操作，需要中间增加一层指针的间接调用，增加开销。
 3. 通过间接访问，增加了阅读代码的难度，同是引入类似m_cloudImp->的前缀，程序员书写代码也变得复杂。

 不过就以上说的缺点，对于优点来说，也是说的过去的，凡事都有两面性嘛！

 */
/*
对于友元函数造成的编译错误或者警告, 解决办法: 要么试着将友元函数写到类内部内联, 要么就写到实现文件中。
在这里pimpl写法中, 类的友元函数写到实现文件中, 而类中的实现类的友元函数写到自己的类内即可。

Widget.cpp:53:15: warning: ‘std::ostream& Widgets::operator<<(std::ostream&, const Widgets::Widget&)’ has not been declared within ‘Widgets’
 std::ostream& Widgets::operator<<(std::ostream& os, const Widgets::Widget& widget) {
               ^~~~~~~
In file included from Widget.cpp:7:
Widget.hpp:24:30: note: only here as a ‘friend’
         friend std::ostream& operator<<(std::ostream& os, const Widget& widget);
                              ^~~~~~~~

 */



#include <cstdlib>
#include <memory>
#include <iostream>
#include <vector>
#include "Widget.hpp"


int main() {
    std::shared_ptr<int> ptr = std::make_unique<int>( 10 );
    std::cout << "*ptr: " << *ptr << std::endl;

    using Widgets::Widget;
    Widget widget;
    std::cout << widget << std::endl;

    std::vector<double> vector( {1, 2, 3, 4} );
    Widget widget1( "干", vector );
    std::cout << widget1 << std::endl;


    std::cout << "结束" << std::endl;
    std::exit( 0 );
}